feat: create @launchdarkly/js-contract-test-utils package#1163
Conversation
Create a new shared package at packages/tooling/contract-test-utils/ that consolidates duplicated contract test code across browser, React Native, Electron, server-node, and Shopify Oxygen SDKs. The package provides: - Universal types (CommandParams, ConfigParams) with no SDK dependency - Client-side utilities (TestHook, TestHarnessWebSocket) for browser-like envs - Server-side utilities (TestHook, ClientPool) for Node.js environments - REST-to-WebSocket adapter bridge for browser/RN contract tests - CLI executable (sdk-testharness-server / sts) with config file support - Subpath exports organized by platform (., ./client, ./server, ./adapter) This is a private package used only within the monorepo. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
@launchdarkly/js-sdk-common size report |
|
@launchdarkly/browser size report |
|
@launchdarkly/js-client-sdk-common size report |
|
@launchdarkly/js-client-sdk size report |
…de hook) Remove out-of-scope code that belongs to other SDK-1866 subtasks: - Remove server-side TestHook (got-based) - Remove REST-to-WebSocket adapter bridge + CLI - Remove TestHarnessWebSocket implementation - Remove ContractTestConfig type - Remove tsconfig.server.json and tsconfig.adapter.json - Remove got, ws, express, cors, body-parser, node-server-sdk dependencies - Update package.json exports (only . and ./client remain) - Update README to reflect scoped-down package Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…owser contract tests - Replace local compat.ts types with imports from @launchdarkly/js-client-sdk-common - Migrate browser contract test entity to import CommandParams, ConfigParams, makeLogger, and TestHook from @launchdarkly/js-contract-test-utils/client - Delete duplicated local files from browser entity (CommandParams.ts, ConfigParams.ts, makeLogger.ts, TestHook.ts) - Add @launchdarkly/js-contract-test-utils dependency to browser entity Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
|
@cursor review |
Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
joker23
left a comment
There was a problem hiding this comment.
it looks like we are only migrating the contract tests in browser... we should check if we can also migrate the contract tests in electron and react-native sdks as they are also based on client side sdks
…deps, convert enums to const objects - Fix dependency versions to use workspace:^ format in browser entity package.json - Convert CommandType, ValueType, HookStage from enums to const objects with type unions (repo guideline) - Consolidate HookStage definition in CommandParams.ts (remove duplicate from ConfigParams.ts) - Migrate electron contract tests to use @launchdarkly/js-contract-test-utils/client - Migrate react-native contract tests to use @launchdarkly/js-contract-test-utils/client - Delete local CommandParams.ts, ConfigParams.ts, makeLogger.ts, TestHook.ts from electron and react-native Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
|
Done — both Also converted the enums ( |
…tests Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
- Delete local CommandParams.ts, ConfigParams.ts, makeLogger.ts, TestHook.ts - Update ClientEntity.ts, ClientRoot.tsx, TestHarnessWebSocket.ts imports - Add @launchdarkly/js-contract-test-utils dependency with workspace:^ Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…compatibility Next.js Turbopack resolves modules from TypeScript source directly and cannot resolve .js extensions pointing to .ts files. Remove all .js extensions from internal imports since the package is consumed as TypeScript source. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
Metro bundler doesn't resolve package.json 'exports' subpaths by default. Enable unstable_enablePackageExports so Metro can resolve @launchdarkly/js-contract-test-utils/client subpath import. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
… patterns Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
….ai/proxy/github.com/launchdarkly/js-core into devin/SDK-2008-1773078228
| * @param id - The unique identifier for the client. | ||
| * @param client - The client entity to store. | ||
| */ | ||
| add(id: string, client: T): void { |
There was a problem hiding this comment.
Should add just return an ID and internally call nextId?
There was a problem hiding this comment.
Good call — updated add() to call nextId() internally and return the assigned ID. The signature is now add(client: T): string instead of add(id: string, client: T): void. Since ClientPool isn't consumed by any migration yet, this is a safe API change.
…the ID Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
🤖 I have created a release *beep* *boop* --- <details><summary>browser: 0.1.12</summary> ## [0.1.12](browser-v0.1.11...browser-v0.1.12) (2026-03-16) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/js-client-sdk bumped from 4.3.1 to 4.4.0 </details> <details><summary>browser-telemetry: 1.0.28</summary> ## [1.0.28](browser-telemetry-v1.0.27...browser-telemetry-v1.0.28) (2026-03-16) ### Dependencies * The following workspace dependencies were updated * devDependencies * @launchdarkly/js-client-sdk bumped from 4.3.1 to 4.4.0 </details> <details><summary>jest: 1.0.7</summary> ## [1.0.7](jest-v1.0.6...jest-v1.0.7) (2026-03-16) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/react-native-client-sdk bumped from ~10.14.1 to ~10.15.0 </details> <details><summary>js-client-sdk: 4.4.0</summary> ## [4.4.0](js-client-sdk-v4.3.1...js-client-sdk-v4.4.0) (2026-03-16) ### Features * Add experimental FDv2 configuration (unused) ([#1169](#1169)) ([c7130cc](c7130cc)) * create @launchdarkly/js-contract-test-utils package ([#1163](#1163)) ([2de5c27](2de5c27)) ### Bug Fixes * Allow 0 status code to be handled by the streaming error filter. ([d96b46b](d96b46b)) * Improve error handling for FDv2 streaming ([d96b46b](d96b46b)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/js-client-sdk-common bumped from 1.21.0 to 1.22.0 </details> <details><summary>js-client-sdk-common: 1.22.0</summary> ## [1.22.0](js-client-sdk-common-v1.21.0...js-client-sdk-common-v1.22.0) (2026-03-16) ### Features * Add experimental FDv2 configuration (unused) ([#1169](#1169)) ([c7130cc](c7130cc)) * Add explicit disableCache setting. ([6be89dd](6be89dd)) * Add FDv1 polling synchronizer for FDv2 fallback (SDK-1923) ([#1159](#1159)) ([498216a](498216a)) * Add mode resolution table for FDv2. ([#1146](#1146)) ([ab2436d](ab2436d)) * FDv2 Cache Initializer ([#1147](#1147)) ([7d6299f](7d6299f)) ### Bug Fixes * Allow 0 status code to be handled by the streaming error filter. ([d96b46b](d96b46b)) * Fix the calculation of the basis parameter for FDv2 streaming. (Does not affect FDv1). ([#1165](#1165)) ([bbdd6c6](bbdd6c6)) * Improve error handling for FDv2 streaming ([d96b46b](d96b46b)) * Max cached context enforcement wasn't working for 0. ([6be89dd](6be89dd)) * rename FDv2 object kind from `flagEval` to `flag-eval` ([#1185](#1185)) ([cd4b119](cd4b119)) </details> <details><summary>react-native-client-sdk: 10.15.0</summary> ## [10.15.0](react-native-client-sdk-v10.14.1...react-native-client-sdk-v10.15.0) (2026-03-16) ### Features * Add experimental FDv2 configuration (unused) ([#1169](#1169)) ([c7130cc](c7130cc)) * create @launchdarkly/js-contract-test-utils package ([#1163](#1163)) ([2de5c27](2de5c27)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/js-client-sdk-common bumped from 1.21.0 to 1.22.0 </details> <details><summary>server-sdk-ai: 0.16.6</summary> ## [0.16.6](server-sdk-ai-v0.16.5...server-sdk-ai-v0.16.6) (2026-03-16) ### Bug Fixes * Make defaultValue optional with a disabled default ([#1144](#1144)) ([e46769b](e46769b)) </details> <details><summary>server-sdk-ai-langchain: 0.5.2</summary> ## [0.5.2](server-sdk-ai-langchain-v0.5.1...server-sdk-ai-langchain-v0.5.2) (2026-03-16) ### Dependencies * The following workspace dependencies were updated * devDependencies * @launchdarkly/server-sdk-ai bumped from ^0.16.5 to ^0.16.6 * peerDependencies * @launchdarkly/server-sdk-ai bumped from ^0.15.0 || ^0.16.0 to ^0.16.6 </details> <details><summary>server-sdk-ai-openai: 0.5.2</summary> ## [0.5.2](server-sdk-ai-openai-v0.5.1...server-sdk-ai-openai-v0.5.2) (2026-03-16) ### Dependencies * The following workspace dependencies were updated * devDependencies * @launchdarkly/server-sdk-ai bumped from ^0.16.5 to ^0.16.6 * peerDependencies * @launchdarkly/server-sdk-ai bumped from ^0.15.0 || ^0.16.0 to ^0.16.6 </details> <details><summary>server-sdk-ai-vercel: 0.5.2</summary> ## [0.5.2](server-sdk-ai-vercel-v0.5.1...server-sdk-ai-vercel-v0.5.2) (2026-03-16) ### Dependencies * The following workspace dependencies were updated * devDependencies * @launchdarkly/server-sdk-ai bumped from ^0.16.5 to ^0.16.6 * peerDependencies * @launchdarkly/server-sdk-ai bumped from ^0.15.0 || ^0.16.0 to ^0.16.6 </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Primarily automated release/version bumps and changelog updates across multiple packages; no functional code changes beyond embedded SDK version constants and dependency bumps. > > **Overview** > Publishes a new release across the monorepo by bumping versions in `.release-please-manifest.json`, updating package `CHANGELOG.md`s, and syncing `package.json` dependency ranges. > > Notable bumps include `@launchdarkly/js-client-sdk` to `4.4.0` (and `@launchdarkly/js-client-sdk-common` to `1.22.0`), `@launchdarkly/react-native-client-sdk` to `10.15.0`, `@launchdarkly/server-sdk-ai` to `0.16.6`, and corresponding provider/aggregator packages (`server-sdk-ai-*`, `@launchdarkly/browser`, `browser-telemetry`, `@launchdarkly/jest`). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit a6d9634. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
BEGIN_COMMIT_OVERRIDE
ci: consolidate contract test utils to @launchdarkly/js-contract-test-utils package
END_COMMIT_OVERRIDE
Requirements
Related issues
devin/SDK-1866-1772645970Describe the solution you've provided
Creates a new private package
@launchdarkly/js-contract-test-utilsatpackages/tooling/contract-test-utils/that consolidates duplicated contract test infrastructure shared across client-side SDKs, and migrates browser, electron, react-native, and React SDK contract tests to consume it.Package exports
.CommandParams,ConfigParams),makeLogger,ClientPool.ts./clientTestHook(fetch-based).tsAll types import
LDContext/LDEvaluationReason/LDLoggerfrom@launchdarkly/js-client-sdk-commonfor cross-SDK compatibility.Migrations included
For each of browser, electron, react-native, and React contract test entities:
CommandParams.ts,ConfigParams.ts,makeLogger.ts,TestHook.tsClientEntity.ts,ClientFactory.ts,TestHarnessWebSocket.ts,ClientRoot.tsxas applicable) to import from@launchdarkly/js-contract-test-utils/client"@launchdarkly/js-contract-test-utils": "workspace:^"dependencyReview feedback addressed
CommandType,ValueType,HookStagefrom TypeScriptenumtoas constobjects with derived union types per repo guidelines"*"to"workspace:^"HookStagedefinition (was in bothCommandParams.tsandConfigParams.ts, now only inCommandParams.ts).jsextensions from shared package internal imports for Next.js Turbopack compatibility (React SDK usesnext buildwith Turbopack, which resolves from.tssource and can't find.jstargets)unstable_enablePackageExportsin react-native entity'smetro.config.jsso Metro can resolve the./clientsubpath exportClientPool.nextId()off-by-one: now returns current counter value before incrementing, so the first ID is"0"(matching existing consumer patterns)ClientPool.add()API: now callsnextId()internally and returns the assigned ID (add(client: T): stringinstead of requiring the caller to pass an ID)Human review checklist
packages/tooling/contract-test-utils/README.mdshows oldClientPoolusage (pool.nextId()thenpool.add(id, entity)) — should be updated to use the newpool.add(entity)API that returns the IDas consttype compatibility: VerifyCommandType,ValueType, andHookStageunion types derived fromas constobjects are assignable everywhere the oldenum/typeunion was used (especially in switch statements and type narrowing)unstable_enablePackageExports: Confirm this doesn't cause unexpected resolution changes for other dependencies in the react-native entityItems for reviewer attention
.tssource files (no compileddist/). Internal imports use extensionless paths for compatibility with both bundlers (Vite, esbuild, Metro) and Next.js Turbopack. If a future consumer requires compiled ESM output,.jsextensions would need to be added back (per Node.js ESM spec), but current consumers work correctly with source-only resolution.ClientPoolutility is exported but not yet consumed.package.jsonexportsfield by default. Enabledunstable_enablePackageExportsin the react-native entity'smetro.config.jsto resolve@launchdarkly/js-contract-test-utils/client. This flag is widely supported in Metro 0.81+ but remains marked "unstable" - please verify it doesn't cause unexpected issues with other package resolutions.Describe alternatives you've considered
type CommandType = 'evaluate' | 'evaluateAll' | ...) but this would break theCommandType.EvaluateFlagaccess pattern used throughout all consumer files. Theas constobject pattern preserves both the named access and the string union type.@launchdarkly/electron-client-sdk) but this would create circular dependencies. Using@launchdarkly/js-client-sdk-commonkeeps the shared package independent..jsextensions in imports (per Node.js ESM spec) but Turbopack cannot resolve these when consuming TypeScript source. Extensionless imports work for all current consumers (bundlers + Turbopack) and are acceptable since the package is private and source-only.mainfield to package.json for Metro compatibility, but enablingunstable_enablePackageExportsis the recommended approach and cleaner long-term.Additional context
workspace:^) for monorepo dependenciesUpdates since last revision
ClientEntity.ts,ClientRoot.tsx,TestHarnessWebSocket.ts).jsextensions from shared package internal importsunstable_enablePackageExportsinmetro.config.jsClientPool.nextId()off-by-one bug to match existing consumer patterns (first ID is now"0"instead of"1")ClientPool.add()to internally callnextId()and return the assigned ID (API changed fromadd(id: string, client: T): voidtoadd(client: T): string)Link to Devin Session: https://app.devin.ai/sessions/f541db4796ce485a892086b8ea79bf95
Requested by: @joker23
Note
Medium Risk
Touches multiple contract-test runners and introduces package
exports/Metro resolution changes; low production impact but moderate risk of breaking test builds across platforms due to module-resolution and shared type changes.Overview
Introduces a new private workspace package,
@launchdarkly/js-contract-test-utils, to centralize shared contract-test infrastructure (common command/config types,makeLogger, a fetch-based client-sideTestHook, and a genericClientPool), using@launchdarkly/js-client-sdk-commonfor cross-SDK type compatibility and exposing a./clientsubpath export.Migrates the browser, Electron, React Native, and React contract-test entities to consume these shared exports, deleting their duplicated local type/logger/hook files, updating imports/dependencies accordingly, and enabling Metro
unstable_enablePackageExportsso React Native can resolve the./clientsubpath.Written by Cursor Bugbot for commit 2ab4f54. This will update automatically on new commits. Configure here.